package chordcows;


import java.io.Serializable;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.Set;

import de.uniba.wiai.lspi.chord.data.URL;
import de.uniba.wiai.lspi.chord.service.Chord;
import de.uniba.wiai.lspi.chord.service.ServiceException;


public class FileServer{
	public static final String MASTER_SYNC = "master_file";
	private final StringKey masterSync = new StringKey(MASTER_SYNC);
	
	private URL localURL;
	private Chord chord=null;
	private int port; 
	private FileUtility util;
	@SuppressWarnings("unused")
	private RMIserver downloader=null;
	private TransferServer transferServer;
	private int transferPort;
	
	public static final String MY_IP = "169.254.209.102";
	
	
	public FileServer(String fileDir){
		//chord stuff
		try{
		de.uniba.wiai.lspi.chord.service.PropertiesLoader.loadPropertyFile();
		}catch(IllegalStateException e){}//do nothing. more than one node on same machine
		port = 8080;
		String protocol = URL.KNOWN_PROTOCOLS.get( URL.SOCKET_PROTOCOL );
		try {
			localURL = new URL ( protocol + "://"+MY_IP+":8080/" );
		} catch ( MalformedURLException e ) {
			throw new RuntimeException ( e );
		}
		
		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl();
		
		//chris's server object
		try{
			downloader = new RMIserver(fileDir,chord,this);
		}catch(RemoteException e){
			//TODO when does this occur?
			e.printStackTrace();
		}
		transferPort=2222;
		
		util = new FileUtility(fileDir);
		
	}
	
	public FileServer(String fileDir, int port){
		try{
			de.uniba.wiai.lspi.chord.service.PropertiesLoader.loadPropertyFile();
			}catch(IllegalStateException e){}//do nothing. more than one node on same machine
		String protocol = URL.KNOWN_PROTOCOLS.get( URL.SOCKET_PROTOCOL );
		this.port=port;
		
		try {
			localURL = new URL ( protocol + "://"+MY_IP+":"+port+"/" );
		} catch ( MalformedURLException e ) {
			throw new RuntimeException ( e );
		}
		
		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl();
		
		//chris's server object
		try{
			downloader = new RMIserver(fileDir, chord, this);
		}catch(RemoteException e){
			//TODO when does this occur?
			e.printStackTrace();
		}
		transferPort=2222;
		
		util = new FileUtility(fileDir);
	}
	
	public FileServer(String fileDir, int port, int RMIport, int transferPort){
		try{
			de.uniba.wiai.lspi.chord.service.PropertiesLoader.loadPropertyFile();
			}catch(IllegalStateException e){}//do nothing. more than one node on same machine
		String protocol = URL.KNOWN_PROTOCOLS.get( URL.SOCKET_PROTOCOL );
		this.port=port;
		
		try {
			localURL = new URL ( protocol + "://"+MY_IP+":"+port+"/" );
		} catch ( MalformedURLException e ) {
			throw new RuntimeException ( e );
		}
		
		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl();
		
		//chris's server object
		try{
			downloader = new RMIserver(fileDir, chord, this, RMIport);
		}catch(RemoteException e){
			//TODO when does this occur?
			e.printStackTrace();
		}
		this.transferPort=transferPort;
		
		util = new FileUtility(fileDir);
	}
	
	public FileServer(String fileDir, int port, int RMIport, int transferPort, boolean hmm){
		try{
			de.uniba.wiai.lspi.chord.service.PropertiesLoader.loadPropertyFile();
			}catch(IllegalStateException e){}//do nothing. more than one node on same machine
		String protocol = URL.KNOWN_PROTOCOLS.get( URL.LOCAL_PROTOCOL );
		this.port=port;
		
		try {
			localURL = new URL ( protocol + "://"+MY_IP+":"+port+"/" );
		} catch ( MalformedURLException e ) {
			throw new RuntimeException ( e );
		}
		
		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl();
		
		//chris's server object
		try{
			downloader = new RMIserver(fileDir, chord, this, RMIport);
		}catch(RemoteException e){
			//TODO when does this occur?
			e.printStackTrace();
		}
		this.transferPort=transferPort;
		
		util = new FileUtility(fileDir);
	}
	
	public void start(){
		System.out.println("Start server");
		try{
			util.sync(getFileKeys());
		}catch(ServiceException e){
			System.out.println("WARNING: Master file not found");
			System.out.println("Creating master file");
			createMasterSync();
		}
		
		seedFiles();
		
		transferServer = new TransferServer(util.getFileDir(),transferPort);
		Thread transferThread = new Thread(transferServer);
		transferThread.start();
		//TODO start chang's thread
		
	}
	
	public void joinNetwork(URL bootstrapURL){
		
//		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl();
		try {
			chord.join( localURL , bootstrapURL );
		} catch ( ServiceException e ) {
			throw new RuntimeException ( " Could not join DHT ! " , e );
		}
		
		System.out.println("Joined Network Successfully");
	}
	
	public void createNetwork(){
		
//		chord = new de.uniba.wiai.lspi.chord.service.impl.ChordImpl() ;
		try {
			chord.create( localURL ) ;
		} catch ( ServiceException e ) {
			throw new RuntimeException ( " Could not create DHT ! " , e ) ;
		}
		System.out.println("Created Network Successfully");
	}
	
	public void leave(){
		//TODO clean my references in hash
		try {
			chord.leave();
		} catch (ServiceException e) {
			e.printStackTrace();
		}
	}
	
	public void seedFiles(){
		String[] files = util.getRealFiles();
		for(String file: files){
			seedFile(file);
		}
	}
	
	public void seedFile(String file){
		try{
			chord.insert(new StringKey(file),this.getServerAddress());
		}catch(ServiceException e){
			System.out.println("WARNING: Failed to seed file: "+file);
		}
	}
	
	public void createMasterSync(){

		String[] myFiles = util.getAllLocalFiles();//TODO currently adds all files, including stubs
		
		try{
			for(String file: myFiles){
				System.out.println(file+" "+masterSync);
				if(file==null)
					System.err.println("its null");
				chord.insert(masterSync, file);
			}
		}catch(ServiceException e){
			e.printStackTrace();
			System.out.println("ERROR: Insert failed while creating master file");
		}
	}
	
	public Set<Serializable> getFileKeys() throws ServiceException{
		return chord.retrieve(masterSync);
	}
	
	public void syncServer(){
		try{
			util.sync(getFileKeys());
		}catch(ServiceException e){
			System.out.println("Sync failed. Master file dead?");
		}
	}
	
	public void addToMaster(String filename){
		try{
			chord.insert(masterSync, filename);
			if(util.exists(filename))
				seedFile(filename);
			else{
				System.out.println("WARNING: Added to master file, but not seeded");
				System.out.println("Does file exists?");
			}
			
		}catch(ServiceException e){
			System.out.println("Failed to add to master file.");
			System.out.println("Master File Dead??");
		}
	}
	
	public String getServerAddress(){
		
		
		return MY_IP+ "*" + transferPort;
	}
	
	public void displayMaster(){
		System.out.println("Displaying master");
		try{
			for(Serializable fileString: this.getFileKeys())
				System.out.println(fileString);
		}catch(ServiceException e){
			System.out.println("Could not get master");
		}
	}
	
}
